package com.biz.primus.app.erpbridge.service;

import com.biz.primus.app.erpbridge.feign.OmsFeignClient;
import com.biz.primus.app.erpbridge.trans.SaleOrderTransformer;
import com.biz.primus.app.erpbridge.vo.oms.ErpSaleOrderState;
import com.biz.primus.app.erpbridge.vo.oms.OmsView;
import com.biz.primus.app.erpbridge.vo.oms.SaleOrder;
import com.biz.primus.app.erpbridge.vo.oms.SaleOrderHeader;
import com.biz.primus.app.erpbridge.vo.oms.request.OmsDetailQueryVo;
import com.biz.primus.app.erpbridge.vo.oms.request.OmsListQueryVo;
import com.biz.primus.base.exception.BizSilentException;
import com.biz.primus.model.oms.enums.OrderPaymentState;
import com.biz.primus.model.oms.enums.SpecialOrderMark;
import com.biz.primus.model.oms.enums.returns.ReturnState;
import com.biz.primus.model.oms.exception.OmsExceptions;
import com.biz.primus.model.oms.stream.OrderStateChannel;
import com.biz.primus.model.oms.stream.ReturnStateChannel;
import com.biz.primus.model.oms.vo.OrderQueryVo;
import com.biz.primus.model.oms.vo.OrderReturnQueryVo;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.biz.primus.model.oms.enums.OrderState.*;

/**
 *
 * 订单查询缓存
 *
 * @author yanweijin
 * @date 2017/11/25
 */

@Service
public class OmsQueryCacheService {

	@Autowired
	private OmsFeignClient omsFeignClient;

	private LoadingCache<String,List<SaleOrder>> ordersCache;
	private LoadingCache<String,List<SaleOrder>> returnsCache;

	private SaleOrderTransformer saleOrderTransformer = SaleOrderTransformer.INSTANCE;

	private Logger log = LoggerFactory.getLogger(this.getClass());

	//订单缓存 20秒
	@Value("${primus.bridge.oms-query-cache.orders-cache-invalidation-seconds:20}")
	private Integer ordersCacheInvalidationSeconds = 20;
	//退单缓存 1分钟
	@Value("${primus.bridge.oms-query-cache.returns-cache-invalidation-seconds:120}")
	private Integer returnsCacheInvalidationSeconds = 60;


	/**
	 * 查询订单列表,不分页
	 * @param req
	 * @return
	 */
	public List<SaleOrderHeader> queryOrders(OmsListQueryVo req){
		return findAllOrders(req.getDepot()).stream()
				.filter(o->this.isMatchCode(o,req.getCodeLike(),true))
				.filter(o->this.isMatchState(o, req.getView(), req.getPresaleEventId()))
				.filter(o->this.isMatchDate(o, req.isOnlyToday()))
				.map(SaleOrderHeader::new)
				.collect(Collectors.toList());
	}

	public SaleOrder queryOrderDetail(OmsDetailQueryVo req){
		Optional<SaleOrder> order = findAllOrders(req.getDepot()).stream()
				.filter(o -> this.isMatchCode(o, req.getDepot(), false))
				.filter(o -> o.getType() == req.getType())
				.findAny();
		if(order.isPresent()){
			return order.get();
		}
		throw new BizSilentException(OmsExceptions.ORDER_NOT_FOUNT, "订单或退单不存在");
	}


	private boolean isMatchDate(SaleOrder o, boolean onlyToday) {
		return !onlyToday || o.getOmsCreateTime().isAfter(LocalDate.now().atStartOfDay());
	}


	private boolean isMatchCode(SaleOrder o, String code, boolean fuzzy){
		if(StringUtils.isNotBlank(code)){
			return fuzzy ? StringUtils.contains(o.getCode(),code) : Objects.equals(o.getCode(),code);
		}
		return true;
	}

	private boolean isMatchState(SaleOrder o, OmsView view, String presaleEventId){
		ErpSaleOrderState state = o.getState();
		switch (view) {
			case all:
				return state !=null;
			case for_print:
				return state == ErpSaleOrderState.allocated;
			case for_outbound:
				return state == ErpSaleOrderState.printed;
			case outbound:
				return state == ErpSaleOrderState.outbound;
			case for_payment:
				return state == ErpSaleOrderState.outbound && o.getPaymentState() == OrderPaymentState.cash;
			case for_inbound:
				return state == ErpSaleOrderState.for_inbound;
			case for_presale:
				if(o.getSpecialOrderMark() == SpecialOrderMark.presale && state != ErpSaleOrderState.for_inbound){
					//检查是否根据预售活动进行查询
					if(StringUtils.isNotBlank(presaleEventId)){
						return StringUtils.contains(o.getSpecialEventId(), presaleEventId);
					}
					return true;
				}
				return false;
			default:
				log.error("无效的view, 没有做对应状态映射");
				return false;
		}
	}


	/**
	 * 从缓存中获取指定门店的所有单据
	 * @param depot
	 * @return
	 */
	private List<SaleOrder> findAllOrders(String depot){
		ArrayList<SaleOrder> result = Lists.newArrayList();
		result.addAll(ordersCache.getUnchecked(depot));
		try {
			result.addAll(returnsCache.get(depot));
		} catch (ExecutionException e) {
			log.error("从缓存加载退货单异常:",e);
		}
		return result;
	}




	/**
	 * 构建缓存
	 */
	@PostConstruct
	void buildCache(){
		ordersCache = CacheBuilder.newBuilder().expireAfterWrite(ordersCacheInvalidationSeconds, TimeUnit.SECONDS).softValues()
				.build(new CacheLoader<String, List<SaleOrder>>() {
					@Override
					public List<SaleOrder> load(String key) throws Exception {
						return queryOrders(key).collect(Collectors.toList());
					}
				});
		returnsCache = CacheBuilder.newBuilder().expireAfterWrite(returnsCacheInvalidationSeconds, TimeUnit.SECONDS).softValues()
				.build(new CacheLoader<String, List<SaleOrder>>() {
					@Override
					public List<SaleOrder> load(String key) throws Exception {
						return queryReturns(key).collect(Collectors.toList());
					}
				});
	}

	/**
	 * 从服务查门店的所有订单(已分单-已出库)
	 * @param depotCode
	 * @return
	 */
	private Stream<SaleOrder> queryOrders(String depotCode){
		OrderQueryVo q = new OrderQueryVo();
		q.setDepotCode(depotCode);
		q.setOrderState(Lists.newArrayList(allocated, printed, outbound));
		q.setPage(0);
		q.setSize(100);
		return omsFeignClient.findOrders(q).stream().map(saleOrderTransformer::formOmsOrderVo);
	}

	/**
	 * 从服务查门店的所有退单(退货中的)
	 * @param depotCode
	 * @return
	 */
	private Stream<SaleOrder> queryReturns(String depotCode){
		OrderReturnQueryVo q = new OrderReturnQueryVo();
		q.setDepotCode(depotCode);
		q.setReturnStatus(Lists.newArrayList(ReturnState.returning));
		q.setPage(0);
		q.setSize(100);
		return omsFeignClient.findReturns(q).stream().map(saleOrderTransformer::formOmsOrderReturnVo);
	}

	/**
	 * 从消息队列接收状态变化的信息,清空订单缓存
	 * @param notice
	 */
	@StreamListener(OrderStateChannel.ORDER_STATE_CHANGE)
	void invalidateOrderCache(OrderStateChannel.OrderStateChangeNotice notice){
		ordersCache.invalidate(notice.getDepotCode());
	}

	/**
	 * 从消息队列接收状态变化的信息,清空退单缓存
	 * @param notice
	 */
	@StreamListener(ReturnStateChannel.RETURN_STATE_CHANGE)
	void invalidateOrderCache(ReturnStateChannel.ReturnStateChangeNotice notice){
		returnsCache.invalidate(notice.getDepotCode());
	}




}
